Перейти к основному содержимому

3.07. Процедуры SQL

Разработчику Аналитику Тестировщику
Архитектору Инженеру

Процедуры SQL

О процедурах

В контексте SQL и реляционных баз данных термин «процедура» почти всегда означает хранимую процедуру (stored procedure) — программный объект, хранящийся в каталоге СУБД и выполняемый на сервере. Понятия «нехранимой процедуры» в классическом смысле не существует, поскольку ключевая характеристика процедуры в СУБД — её сохранение в базе данных для повторного использования. Временные или динамические блоки кода, не сохранённые в виде объекта БД, не считаются процедурами. Хранимые процедуры (Stored Procedures) – мощный инструмент SQL для для создания предопределённых операций, которые выполняются непосредственно на сервере базы данных.

Хранимые процедуры можно классифицировать по нескольким признакам:

  1. По типу реализации
    • SQL-процедуры — написаны на диалекте SQL (PL/pgSQL, T-SQL, PL/SQL и т.д.), используют стандартные SQL-операторы и расширения (циклы, условия).
    • Процедуры на языках общего назначения — в некоторых СУБД (например, SQL Server с CLR, PostgreSQL с функциями на C/Python) допускается реализация на внешних языках.
  2. По способу вызова
    • Именованные процедуры — создаются с помощью CREATE PROCEDURE и вызываются по имени.
    • Анонимные блоки — в PL/SQL (Oracle) поддерживаются блоки без имени, но они не являются «процедурами» в строгом смысле.
  3. По уровню доступа
    • Системные процедуры — предоставляемые СУБД для администрирования (например, sp_help, sp_rename в SQL Server).
    • Пользовательские процедуры — созданные разработчиком под конкретную логику.
  4. По параметрам. Процедуры могут принимать:
    • Входные параметры (IN)
    • Выходные (OUT)
    • Входные и выходные (INOUT / IN OUT)

Пример (PostgreSQL):

CREATE PROCEDURE transfer_funds(
IN from_account INT,
IN to_account INT,
IN amount DECIMAL,
OUT status TEXT
)
AS $$
BEGIN
-- Логика перевода
UPDATE accounts SET balance = balance - amount WHERE id = from_account;
UPDATE accounts SET balance = balance + amount WHERE id = to_account;
status := 'OK';
END;
$$ LANGUAGE plpgsql;

Хранимая процедура – это набор SQL-операций, сохранённый в БД, именованный блок кода, который можно вызывать по имени. Это объект БД, который может принимать параметры и возвращать результаты, аналог функций в языках программирования, но работающий внутри СУБД.

Хранимые процедуры нужны для следующих целей:

  • повторное использование кода – один раз написал, много раз вызвал;
  • безопасность – можно давать права на выполнение процедуры без доступа к таблицам;
  • производительность – выполняются на сервере, минимизация сетевого трафика;
  • централизованная логика – изменение в одном месте влияет на все приложения;
  • сокращение SQL-инъекций – параметры передаются безопасно.

Общая структура процедур:

CREATE [OR REPLACE] PROCEDURE procedure_name ([параметры])
[характеристики]
BEGIN
-- SQL-операции
END;

Пример:

DELIMITER //
CREATE PROCEDURE GetEmployee(IN emp_id INT)
BEGIN
SELECT * FROM employees WHERE id = emp_id;
END //
DELIMITER ;

Параметры – значения, которые принимаются процедурами:

Тип параметраОписаниеПример
INВходной параметр (по умолчанию)IN emp_name VARCHAR(100)
OUTВыходной параметр (возвращает значение)OUT total_employees INT
INOUTВходной и выходной одновременноINOUT counter INT

Пример с параметрами:

CREATE PROCEDURE UpdateSalary(
IN emp_id INT,
IN increase_amount DECIMAL(10,2),
OUT new_salary DECIMAL(10,2)
BEGIN
UPDATE employees
SET salary = salary + increase_amount
WHERE id = emp_id;

SELECT salary INTO new_salary FROM employees WHERE id = emp_id;
END;

Вызов процедур – обращение к уже существующей процедуре, с целью получить результат. Синтаксис вызова зависит от СУБД:

MySQL:

CALL GetEmployee(123);

SQL Server:

EXEC GetEmployee @emp_id = 123;

Oracle:

BEGIN
GetEmployee(123);
END;

Вызов с выходными параметрами:

-- MySQL
CALL UpdateSalary(123, 5000, @new_salary);
SELECT @new_salary;

-- SQL Server
DECLARE @result DECIMAL(10,2);
EXEC UpdateSalary @emp_id=123, @increase_amount=5000, @new_salary=@result OUTPUT;
SELECT @result;

Таким образом, хранимая процедура имеет три ключевых особенности: упрощение кода, безопасность и производительность.

Для выполнения хранимой процедуры вызывается команда EXEC или EXECUTE.


Отличие от функций

Несмотря на схожесть (оба — хранимые программные объекты), между процедурами и функциями есть принципиальные различия:

ХарактеристикаПроцедураФункция
Возвращаемое значениеНе обязана возвращать значение. Может использовать OUT-параметры.Должна возвращать одно значение (скалярное, таблицу и т.д.).
Вызов в выраженияхНельзя вызывать внутри SELECT, WHERE.Можно вызывать в SELECT, если не модифицирует данные.
Оператор RETURNМожет использоваться для раннего выхода, но не возвращает выражение.Используется для возврата значения: RETURN expr;
Модификация данныхРазрешена.В большинстве СУБД запрещена в контексте SELECT (например, в PostgreSQL скалярные функции не должны изменять состояние).
Использование в DMLМожет содержать COMMIT, ROLLBACK (в некоторых СУБД).Обычно не может управлять транзакциями.
Табличные возвращаемые значенияПоддерживается не везде (например, в SQL Server — да, через OUTPUT).Часто используется: табличные функции (RETURNS TABLE) работают как виртуальные таблицы.

Пример функции с возвращаемым значением:

CREATE FUNCTION get_employee_count(dept_id INT) 
RETURNS INT
AS $$
DECLARE cnt INT;
BEGIN
SELECT COUNT(*) INTO cnt FROM employees WHERE department_id = dept_id;
RETURN cnt;
END;
$$ LANGUAGE plpgsql;

-- Использование в запросе:
SELECT get_employee_count(5);

Функции используются для вычислений и преобразований, особенно когда результат нужен в рамках SQL-запроса. Процедуры подходят для выполнения сложных бизнес-операций: пакетная обработка, транзакции, вызов нескольких операторов, работа с курсорами.


Триггеры

Триггер – специальный вид хранимой процедуры, который автоматически выполняется при наступлении определённого события в базе данных. То есть, когда выполняется INSERT, UPDATE, DELETE в какой-то таблице, триггер может автоматически сделать что-то ещё.

Примеры использования – логирование изменений, каскадные обновления и удаления, обновление связанных таблиц, валидация данных до их вставки, автоматическое обновление поля.

Структура триггера включает:

  • Событие – когда запускается триггер (INSERT, UPDATE, DELETE);
  • Таблица – к какой таблице относится;
  • Функция / тело – какие действия выполняются;
  • Момент – до или после события (BEFORE / AFTER).

Пример триггера в PostgreSQL:

CREATE OR REPLACE FUNCTION log_user_update()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO user_log(user_id, action, change_time)
VALUES (OLD.id, 'update', NOW());
RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trigger_user_update
AFTER UPDATE ON users
FOR EACH ROW
EXECUTE FUNCTION log_user_update();

Виды триггеров:

ВидКогда вызываетсяНа что реагирует
BEFORE INSERTДо вставки новой строкиМожет изменить данные перед вставкой
AFTER INSERTПосле вставкиДля обновления другой таблицы
BEFORE UPDATEПеред обновлениемМожно изменить новые значения
AFTER UPDATEПосле обновленияЛогирование, синхронизация
BEFORE DELETEПеред удалениемМожет предотвратить данные
AFTER DELETEПосле удаленияЧистка, каскадное удаление

Триггеры весьма непростой инструмент, и часто может замедлить операции.


Операторы управления потоком в SQL-процедурах

ОператорСинтаксисОписаниеСУБД
DECLAREDECLARE var_name TYPE [DEFAULT value]Объявляет переменнуюMySQL, SQL Server, PostgreSQL, Oracle
SETSET var_name = valueПрисваивает значение переменнойВсе основные СУБД
IF-THEN-ELSEIF condition THEN statements [ELSEIF condition THEN statements] [ELSE statements] END IF;Условное выполнение кодаВсе основные СУБД
CASECASE WHEN condition THEN statements [WHEN condition THEN statements] [ELSE statements] END CASE;Множественное ветвлениеВсе основные СУБД
WHILEWHILE condition DO statements END WHILE;Цикл с предусловиемMySQL, SQL Server, PostgreSQL
REPEATREPEAT statements UNTIL condition END REPEAT;Цикл с постусловиемMySQL, PostgreSQL
LOOPLOOP statements [LEAVE label;] [ITERATE label;] END LOOP;Бесконечный цикл с ручным управлениемMySQL, PostgreSQL
LEAVELEAVE label;Выход из цикла или блока (аналог break)MySQL, PostgreSQL
ITERATEITERATE label;Переход к следующей итерации цикла (аналог continue)MySQL, PostgreSQL
FORFOR var_name IN [REVERSE] start..end [BY step] LOOP statements END LOOP;Цикл с счётчикомPostgreSQL, Oracle
TRY-CATCHBEGIN TRY statements END TRY BEGIN CATCH statements END CATCHОбработка ошибокSQL Server
DECLARE HANDLERDECLARE handler_type HANDLER FOR condition handler_statementsОбработка исключенийMySQL
RETURNRETURN value;Возврат значения из функцииВсе основные СУБД
GOTOGOTO label;Безусловный переходSQL Server, Oracle